﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace SocketClient
{

        public class InteropsHelper
        {
            public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;

            public static void ShowMessageBox(string message, string title)
            {
                int resp = 0;
                WTSSendMessage(
                    WTS_CURRENT_SERVER_HANDLE,
                    WTSGetActiveConsoleSessionId(),
                    title, title.Length,
                    message, message.Length,
                    0, 0, out resp, false);
            }

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern int WTSGetActiveConsoleSessionId();

            [DllImport("wtsapi32.dll", SetLastError = true)]
            public static extern bool WTSSendMessage(
                IntPtr hServer,
                int SessionId,
                String pTitle,
                int TitleLength,
                String pMessage,
                int MessageLength,
                int Style,
                int Timeout,
                out int pResponse,
                bool bWait);

            /// <summary>
            /// 调用进程
            /// </summary>
            /// <param name="app">应用程序名(包含扩展名)</param>
            /// <param name="path">应用程序所在目录</param>
            /// <param name="a_strParam">传入参数(第一个参数有空格)</param>
            public static void CreateProcess(string app, string path, string a_strParam)
            {
                PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
                SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
                IntPtr hDupedToken = IntPtr.Zero;
                try
                {
                    bool result;
                    IntPtr hToken = WindowsIdentity.GetCurrent().Token;

                    sa.Length = Marshal.SizeOf(sa);

                    STARTUPINFO si = new STARTUPINFO();
                    si.cb = Marshal.SizeOf(si);

                    int dwSessionID = WTSGetActiveConsoleSessionId();
                    result = WTSQueryUserToken(dwSessionID, out hToken);

                    //if (!result)//此处有错误
                    //{
                    //    ShowMessageBox("WTSQueryUserToken failed", "AlertService Message");
                    //}

                    result = DuplicateTokenEx(
                          hToken,
                          GENERIC_ALL_ACCESS,
                          ref sa,
                          (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
                          (int)TOKEN_TYPE.TokenPrimary,
                          ref hDupedToken
                       );

                    //if (!result)
                    //{
                    //    ShowMessageBox("DuplicateTokenEx failed", "AlertService Message");
                    //}

                    IntPtr lpEnvironment = IntPtr.Zero;
                    result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);

                    //if (!result)
                    //{
                    //    ShowMessageBox("CreateEnvironmentBlock failed", "AlertService Message");
                    //}

                    result = CreateProcessAsUser(
                                         hDupedToken,
                                         app,
                                         a_strParam,
                                         ref sa, ref sa,
                                         false, 0, IntPtr.Zero,
                                         path, ref si, ref pi);

                    //if (!result)
                    //{
                    //    int error = Marshal.GetLastWin32Error();
                    //    string message = String.Format("CreateProcessAsUser Error: {0}", error);
                    //    ShowMessageBox(message, "AlertService Message");
                    //}
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    if (pi.hProcess != IntPtr.Zero)
                        CloseHandle(pi.hProcess);
                    if (pi.hThread != IntPtr.Zero)
                        CloseHandle(pi.hThread);
                    if (hDupedToken != IntPtr.Zero)
                        CloseHandle(hDupedToken);
                }
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct STARTUPINFO
            {
                public Int32 cb;
                public string lpReserved;
                public string lpDesktop;
                public string lpTitle;
                public Int32 dwX;
                public Int32 dwY;
                public Int32 dwXSize;
                public Int32 dwXCountChars;
                public Int32 dwYCountChars;
                public Int32 dwFillAttribute;
                public Int32 dwFlags;
                public Int16 wShowWindow;
                public Int16 cbReserved2;
                public IntPtr lpReserved2;
                public IntPtr hStdInput;
                public IntPtr hStdOutput;
                public IntPtr hStdError;
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct PROCESS_INFORMATION
            {
                public IntPtr hProcess;
                public IntPtr hThread;
                public Int32 dwProcessID;
                public Int32 dwThreadID;
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct SECURITY_ATTRIBUTES
            {
                public Int32 Length;
                public IntPtr lpSecurityDescriptor;
                public bool bInheritHandle;
            }

            public enum SECURITY_IMPERSONATION_LEVEL
            {
                SecurityAnonymous,
                SecurityIdentification,
                SecurityImpersonation,
                SecurityDelegation
            }

            public enum TOKEN_TYPE
            {
                TokenPrimary = 1,
                TokenImpersonation
            }

            public const int GENERIC_ALL_ACCESS = 0x10000000;

            [DllImport("kernel32.dll", SetLastError = true,
                CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
            public static extern bool CloseHandle(IntPtr handle);

            [DllImport("advapi32.dll", SetLastError = true,
                CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
            public static extern bool CreateProcessAsUser(
                IntPtr hToken,//第一个hToken，是跟用户相关的访问令牌
                string lpApplicationName,//你所创建进程文件的所在路径包含文件名（最好带着扩展名）windows api 把路径转换为短路径
                string lpCommandLine,//放你给被创建进程传递的参数
                ref SECURITY_ATTRIBUTES lpProcessAttributes,
                ref SECURITY_ATTRIBUTES lpThreadAttributes,
                bool bInheritHandle,
                Int32 dwCreationFlags,
                IntPtr lpEnvrionment,
                string lpCurrentDirectory,//指的是进程运行的目录（注意不是进程文件所在的目录），为NULL时默认随父进程
                ref STARTUPINFO lpStartupInfo,
                ref PROCESS_INFORMATION lpProcessInformation);//这个是个出参。 返回被创建进程的信息，包括进程PID等

            [DllImport("advapi32.dll", SetLastError = true)]
            public static extern bool DuplicateTokenEx(
                IntPtr hExistingToken,
                Int32 dwDesiredAccess,
                ref SECURITY_ATTRIBUTES lpThreadAttributes,
                Int32 ImpersonationLevel,
                Int32 dwTokenType,
                ref IntPtr phNewToken);

            [DllImport("wtsapi32.dll", SetLastError = true)]
            public static extern bool WTSQueryUserToken(
                Int32 sessionId,
                out IntPtr Token);

            [DllImport("userenv.dll", SetLastError = true)]
            static extern bool CreateEnvironmentBlock(
                out IntPtr lpEnvironment,
                IntPtr hToken,
                bool bInherit);
        }
}
